/**
 *
 */
package info.angelloznicoud.ircdl.bot;

import info.angelloznicoud.ircdl.bean.DownloadMessage;

import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.HashSet;
import java.util.Set;

import org.apache.log4j.Logger;
import org.jibble.pircbot.DccFileTransfer;
import org.jibble.pircbot.IrcException;
import org.jibble.pircbot.NickAlreadyInUseException;
import org.jibble.pircbot.PircBot;

/**
 * @author ANGELLOZ-NICOUD Sébastien
 *
 * @version 1.0
 */
public class IrcDownloaderBot extends PircBot {

	private static final Logger LOG = Logger.getLogger(IrcDownloaderBot.class);

	private String server;

	private Set<String> channels;

	private String nickName;

	private DccFileTransfer download;

	private DownloadMessage currentDownloadMessage;

	private File outputFolder;

	private boolean verbose;

	public IrcDownloaderBot(String server, String nickName,
			File output, boolean verbose) {
		super();
		this.server       = server;
		this.channels     = new HashSet<String>();
		this.nickName     = nickName;
		this.verbose      = verbose;
		this.outputFolder = output;
	}

	/**
	 * Try to connect the bot to its channel.
	 *
	 * @throws NickAlreadyInUseException
	 * @throws IOException
	 * @throws IrcException
	 */
	public void connect() throws NickAlreadyInUseException, IOException, IrcException {
		LOG.info("Try to connect to chat server : " + this.server);

		setName(this.nickName);
		setVerbose(this.verbose);

		connect(this.server);

		LOG.info("Connected to : " + this.server);
	}

	/**
	 * Make a bot join a channel.
	 *
	 * @param channel - the channel to join.
	 */
	public void joinChan(String channel) {
		String chan = channel;

		if (!this.channels.contains(chan)) {
			LOG.info("Bot joining channel : " + chan);

			this.channels.add(chan);

			if (!chan.startsWith("#")) {
				chan = "#" + chan;
			}

			joinChannel(chan);
		}
	}

	/**
	 * Is the bot downloading a file.
	 *
	 * @return <code>true</code> if yes, <code>false</code> otherwise.
	 */
	public boolean isDownloading() {
		if (this.currentDownloadMessage == null) {
			return false;
		}

		return true;
	}

	/**
	 * Get a displayable message for download progression.
	 *
	 * @return the progression message as a string.
	 */
	public String getDownloadProgress() {
		if (this.isDownloading()) {
			if (this.download != null) {
				DecimalFormat df = new DecimalFormat("#.##");

				return this.download.getFile().getName()
						+ " (" + df.format(this.download.getProgressPercentage())
						+ " %) at "
						+ df.format((this.download.getTransferRate() / 1024.0)) + " ko/s";
			} else {
				return this.currentDownloadMessage.getMessage() + " (0%)";
			}
		}

		return null;
	}

	/**
	 * Start the download by sending a message to the bot.
	 *
	 * @param message - the message object that contains infos.
	 */
	public void startDownload(DownloadMessage message) {
		this.currentDownloadMessage = message;
		sendMessage(message.getBotName(), message.getXdccCommand());
	}

	@Override
	protected void onIncomingFileTransfer(DccFileTransfer transfer) {

		LOG.info("[INCOMING] file transfer : "
				+ transfer.getFile().getName() + " (" + transfer.getSize() + " bytes).");

		this.download = transfer;
		File file = transfer.getFile();
		long size  = transfer.getSize();
		File out  = new File(this.outputFolder.getAbsolutePath() + File.separator + file.getName());

		//File exists, looking if resume is needed.
		if (out.exists()) {
			if (out.length() < size) {
				transfer.receive(out, true);
			} else {
				transfer.close();
			}
		} else {
			transfer.receive(out, false);
		}
	}

	@Override
	protected void onFileTransferFinished(DccFileTransfer transfer, Exception e) {
		this.currentDownloadMessage.getFileManager().onDownloadFinished(this.currentDownloadMessage);

		this.currentDownloadMessage = null;
		this.download = null;

		if (transfer != null) {
			LOG.info("[FINISHED] file transfer : " + transfer.getFile().getName());
		}
	}

	@Override
	protected void onNotice(String sourceNick, String sourceLogin,
			String sourceHostname, String target, String notice) {
		//Trying to cancel XDCC if the transfer is already complete
		if (this.currentDownloadMessage != null) {
			if (sourceNick.equals(this.currentDownloadMessage.getBotName())) {
				if (notice.contains("You have a DCC pending")) {
					LOG.info("[CANCELED] Canceling XDCC Transfer.");
					sendMessage(this.currentDownloadMessage.getBotName(), "XDCC CANCEL");
					onFileTransferFinished(null, null);
				}
			}
		}
	}

	/**
	 * Cancel a XDCC transfer.
	 */
	public void sendXdccCancel() {
		if (this.currentDownloadMessage != null) {
			sendMessage(this.currentDownloadMessage.getBotName(), "XDCC CANCEL");
		}
	}

	/**
	 * Set bot nick name on he channel.
	 *
	 * @param nickName - the (new) nick name.
	 */
	public void setNickName(String nickName) {
		this.nickName = nickName;
	}
}
